#include <cstring>
#include <iostream>

#include "Utility/Configuration/SGConfig.h"
#include "Utility/Configuration/SGParameter.h"
#include "Utility/Logging/SGLogger.h"
#include "Utility/Stacktrace/SGSignal.h"

#include "DataStructure/Input/Element/ElementTypes.h"
#include "DataStructure/Input/Load\FORCE1Data.h"
#include "DataStructure/Input/Solver/DOFDirections.h"
#include "DataStructure/Input/Solver/Subcase.h"

#include "DBManager/DBAPI.h"
#include "DBManager/DBServiceFactory.h"
#include "DBManager/Database/DatabaseFactory.h"
#include "DBManager/IService/Input/Constraint/IConstraintSummaryService.h"
#include "DBManager/IService/Input/Constraint/IIndexMappingService.h"
#include "DBManager/IService/Input/Constraint/IRElementSummaryService.h"
#include "DBManager/IService/Input/Constraint/ISPCService.h"
#include "DBManager/IService/Input/Coordinate/ICORD1RService.h"
#include "DBManager/IService/Input/Coordinate/ICORDSummaryService.h"
#include "DBManager/IService/Input/Element/IElementSummaryService.h"
#include "DBManager/IService/Input/Load/IFORCE1Service.h"
#include "DBManager/IService/Input/Load/ILoadSummaryService.h"
#include "DBManager/IService/Input/Node/INodeService.h"
#include "DBManager/IService/Input/Parameter/IParameterService.h"
#include "DBManager/IService/Input/Property/IPropertySummaryService.h"
#include "DBManager/IService/Input/Subcase/ISubcaseService.h"

#include "Export/ExportAPI/ExportAPI.h"
#include "Import/ImportAPI/ImportAPI.h"

#include "SGFem/Pre/DOFHandler/DOFHandler.h"
#include "SGFem/Pre/DOFHandler/GeneratePartition.h"

#include "Partition/SplitSubCaseGroup.h"
#include "SGFem/Task/Buckling/BucklingSolver.h"
#include "SGFem/Task/Modes/ModalSolver.h"
#include "SGFem/Task/Static/StaticSolver.h"
#include "SGFem/Task/Thermal/ThermalSolver.h"

#ifdef SG_USE_MPI
#include "Utility/Algebra/MpiEnvironment.h"
#endif

using SG::DataStructure::Common::Status;
using SG::DataStructure::FEM::SOLType;
using SG::DataStructure::FEM::DofSet;
using SG::DataStructure::FEM::Field_Types;
using SG::DataStructure::FEM::Project;
using SG::DataStructure::FEM::Subcase;
using SG::FEMSolver::DOFHandler;
using namespace SG::DBManager;
using SG::Export::SGExporter;
using SG::FEMSolver::GeneratePartitioning;
using SG::Import::SGImporter;

int main (int argc, char** argv)
{
    // Register for stack printing
    // SG::Utility::SGSignal::registerSignalHandlers ();

    // todo:  Parser command Line
    // Project project;
    // if (Status::STATUS_Failure == parserCommond (argc, argv, _OUT project))
    // {
    //     help ();
    //     return 0;
    // }
    // printSubmitPrompt (project.m_modelFileName);

    // Database factory
    DatabaseFactory dbFactory;

    // Initialize the Algebra library
    SG::Algebra::Initialize (argc, argv);

    IDatabase* h5db = dbFactory.GetDatabase (DatabaseFactory::HDF5, ".", "main.db");

    // Create a Service Manager based on the database
    auto dbServiceFactory = std::make_shared<DBServiceFactory> (h5db);

    // If the data already exists, clear the previous one
    dbServiceFactory->ClearDatabase ();

    // Import FEM Model
    SGImporter importer (dbServiceFactory);

    auto importStatus = importer.Import ("D:/SGFEM/devtask/SGSim/TestData/SGFem/AnalysisProcess/Force1_cquad4.bdf");
    // auto       importStatus = importer.Import ("D:/SGFEM/devtask/SGSim/TestData/SGFem/AnalysisProcess/Force0_cquad4.bdf");
    // auto       importStatus = importer.Import (project.m_modelFilePath + project.m_modelFileName);
    if (Status::STATUS_Failure == importStatus)
    {
        SGError << "Import Failure !!!";
        return -1;
    }

    SG::Utility::SGParameter parameters = SG::Utility::SGConfig::GetInstance ().GetData ().GetValue ("Partition");
    int                      nparts     = parameters.GetValue ("NParts").GetInt ();
    size_t                   parts      = nparts;

    std::map<int, IDatabase*>                        dbMap;
    std::map<int, std::shared_ptr<DBServiceFactory>> dbfMap;

    for (size_t num = 0; num < parts; num++)
    {
        auto dbx = dbFactory.GetDatabase (DatabaseFactory::HDF5, ".", "Partition_" + std::to_string (num) + ".db");
        dbMap.insert ({ num, dbx });
        auto dbf = std::make_shared<DBServiceFactory> (dbx);
        dbfMap.insert ({ num, dbf });
        dbf->ClearDatabase ();
    }

    // Partition operations
    auto sumService   = dbServiceFactory->get<IElementSummaryService> ();
    auto elementTypes = sumService->GetAllElementTypes ();

    SG::DataStructure::Common::IdArray SGPartitionElement;
    SG::Algebra::Matrix                elementNodeIds;
    SG::DataStructure::Common::IdArray nodeIds;
    SG::DataStructure::Common::IdArray elementIds;
    for (const auto& type : elementTypes)
    {
        elementNodeIds = sumService->GetElementNodes (type);
        elementIds     = sumService->GetAllElementId ();
    }
    nodeIds = dbServiceFactory->get<INodeService> ()->GetAllId ();
    std::vector<std::size_t> Nodeindex (nodeIds.size ());
    for (size_t i = 0; i < nodeIds.size (); ++i)
    {
        Nodeindex[i] = i;
    }
    GeneratePartitioning::SGPartitioner (dbServiceFactory, nodeIds, elementIds, elementNodeIds, parameters, SGPartitionElement);
    // Storage unit information and processing node information
    std::unordered_map<int, std::set<SG::DataStructure::Common::Id>> idsetMap;

    // 某分区内 用到的属性ID
    std::map<int, std::set<SG::DataStructure::Common::Id>> propId;

    for (size_t i = 0; i < elementIds.size (); ++i)
    {
        // Deposit element information
        int to  = SGPartitionElement[i];
        int eid = elementIds[i];
        SG::DBManager::DBAPI::CopyData (
            dbServiceFactory.get (), dbfMap[to].get (), SG::DataStructure::Common::DataTableType::DT_ELEMENT, eid);

        // Process node information
        auto elenode = sumService->FindElementNodes (eid);
        for (size_t j = 0; j < elenode.size (); j++)
        {
            idsetMap[to].insert (elenode[j]);
        }
    }
    // 材料属性存储：根据eid找到对应的Element, 然后找到对应的PID 然后存进去
    for (int to = 0; to < nparts; to++)
    {
        // todo 材料pid信息获取接口待完善
        SG::DBManager::DBAPI::CopyData (
            dbServiceFactory.get (), dbfMap[to].get (), SG::DataStructure::Common::DataTableType::DT_PROPERTY, 1);
        // todo 属性pid信息获取接口待完善
        SG::DBManager::DBAPI::CopyData (
            dbServiceFactory.get (), dbfMap[to].get (), SG::DataStructure::Common::DataTableType::DT_MATERIAL, 1);
    }

    // R单元存储
    for (int to = 0; to < nparts; to++)
    {
        auto sumServiceIRElement = dbServiceFactory->get<IRElementSummaryService> ();
        for (auto&& i : sumServiceIRElement->GetAllRElementId ())
        {
            SG::DBManager::DBAPI::CopyData (
                dbServiceFactory.get (), dbfMap[to].get (), SG::DataStructure::Common::DataTableType::DT_RELEMENT, i);
        }
    }
    // subcase信息存储
    for (int to = 0; to < nparts; to++)
    {
        auto sumServiceISubcase = dbServiceFactory->get<ISubcaseService> ();

        for (auto&& i : sumServiceISubcase->GetAllSubcaseID ())
        {
            SG::DBManager::DBAPI::CopyData (
                dbServiceFactory.get (), dbfMap[to].get (), SG::DataStructure::Common::DataTableType::DT_SUBCASE, i);
        }
    }

    // Deposit node information
    for (size_t i = 0; i < parts; ++i)
    {
        auto idset = idsetMap[i];
        for (auto&& nodeId : idset)
        {
            SG::DBManager::DBAPI::CopyData (
                dbServiceFactory.get (), dbfMap[i].get (), SG::DataStructure::Common::DataTableType::DT_NODE, nodeId);
        }
    }

    // 载荷信息存储
    auto LoadService = dbServiceFactory->get<ILoadSummaryService> ();

    SG::DataStructure::FEM::NodeData                node;
    std::vector<SG::DataStructure::FEM::FORCE1Data> datas;
    // 获取所有id
    auto allId = LoadService->GetAllId ();
    for (auto id : allId)
    {
        // 根据id获取载荷类型
        auto types = LoadService->GetLoadType (id);
        // 遍历所有载荷类型存储 载荷信息 以及 确定载荷加载方向所需的节点信息
        for (auto&& type : types)
        {
            if (type == SG::DataStructure::FEM::LoadType::LOAD_Force1)
            {
                datas.clear ();
                dbServiceFactory->get<IFORCE1Service> ()->FindBySId (id, datas);
                // 遍历所有载荷数据
                for (auto&& data : datas)
                {
                    // 存储到相应的分区中
                    for (size_t to = 0; to < parts; ++to)
                    {
                        auto nodeService = dbfMap[to]->get<INodeService> ();
                        // 如果该分区有加载的节点，那么存储该载荷信息
                        if (nodeService->FindById (data.m_nodeId, node) == SG::DataStructure::Common::Status::STATUS_Success)
                        {
                            dbfMap[to]->get<IFORCE1Service> ()->Add (data);
                            // 同时存储加载方向所需的节点id
                            for (size_t i = 0; i < 2; i++)
                            {
                                if (nodeService->FindById (data.m_gi[i], node) == SG::DataStructure::Common::Status::STATUS_Failure)
                                {
                                    SG::DBManager::DBAPI::CopyData (dbServiceFactory.get (),
                                                                    dbfMap[to].get (),
                                                                    SG::DataStructure::Common::DataTableType::DT_NODE,
                                                                    data.m_gi[i]);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    // 约束信息存储
    auto                                         ConstraintService = dbServiceFactory->get<IConstraintSummaryService> ();
    auto                                         allIdSPC          = ConstraintService->GetAllId ();
    std::vector<SG::DataStructure::FEM::SPCData> dataSpc;
    // 获取所有id
    for (auto id : allIdSPC)
    {
        // 根据id获取约束类型
        auto types = ConstraintService->GetType (id);
        // 遍历所有约束类型 将施加约束节点对应分区 存储约束信息
        for (auto&& type : types)
        {
            if (type == SG::DataStructure::FEM::CONTYPE_SPC)
            {
                dataSpc.clear ();
                dbServiceFactory->get<ISPCService> ()->FindBySId (id, dataSpc);
                for (auto&& data : dataSpc)
                {
                    // 存储到相应的分区中
                    for (size_t to = 0; to < parts; ++to)
                    {
                        auto nodeService = dbfMap[to]->get<INodeService> ();
                        // 如果该分区有约束的节点，那么存储该约束信息
                        if (nodeService->FindById (data.m_g, node) == SG::DataStructure::Common::Status::STATUS_Success)
                        {
                            // 添加约束
                            dbfMap[to]->get<ISPCService> ()->Add (data);
                        }
                    }
                }
            }
        }
    }
    // 坐标系信息存储
    auto CordService = dbServiceFactory->get<ICORDSummaryService> ();
    auto allIdCord   = CordService->GetAllId ();
    // 获取所有id
    for (auto id : allIdCord)
    {
        //存储坐标系信息到所有分区中
        for (int to = 0; to < nparts; to++)
        {
            for (auto&& i : CordService->GetAllCoordinate ())
            {
                SG::DBManager::DBAPI::CopyData (
                    dbServiceFactory.get (), dbfMap[to].get (), SG::DataStructure::Common::DataTableType::DT_COORDINATE, i.get ()->m_id);
            }
        }
        // 根据id获取坐标系类型
        auto type = CordService->GetCoordType (id);
        if (type == SG::DataStructure::FEM::COORDTYPE_Cartesian)
        {
            SG::DataStructure::FEM::CORD1RData dataCord;
            //建立坐标系所需的节点信息存储到相应的分区的节点数据库中
            for (int to = 0; to < nparts; to++)
            {
                auto nodeService = dbfMap[to]->get<INodeService> ();

                dbServiceFactory->get<ICORD1RService> ()->FindById (id, dataCord);
                //判断分区没有该节点信息，那么存储到节点数据库中
                if (nodeService->FindById (dataCord.m_g1, node) == SG::DataStructure::Common::Status::STATUS_Failure)
                {
                    SG::DBManager::DBAPI::CopyData (
                        dbServiceFactory.get (), dbfMap[to].get (), SG::DataStructure::Common::DataTableType::DT_NODE, dataCord.m_g1);
                }
                if (nodeService->FindById (dataCord.m_g2, node) == SG::DataStructure::Common::Status::STATUS_Failure)
                {
                    SG::DBManager::DBAPI::CopyData (
                        dbServiceFactory.get (), dbfMap[to].get (), SG::DataStructure::Common::DataTableType::DT_NODE, dataCord.m_g2);
                }
                if (nodeService->FindById (dataCord.m_g3, node) == SG::DataStructure::Common::Status::STATUS_Failure)
                {
                    SG::DBManager::DBAPI::CopyData (
                        dbServiceFactory.get (), dbfMap[to].get (), SG::DataStructure::Common::DataTableType::DT_NODE, dataCord.m_g3);
                }
            }
        }
    }

    // Group scenarios by boundary conditions and analysis type
    std::vector<SG::DataStructure::Common::IdArray> subcaseGroup;
    SG::splitSubcaseGroup (dbServiceFactory, _OUT subcaseGroup);
    std::size_t                         s_maxNodeDOFSize;
    SG::DataStructure::FEM::Field_Types m_field{ Field_Types::FIELD_Elastic };
    for (auto& group : subcaseGroup)
    {
        auto subcaseId = group[0];
        auto para      = dbServiceFactory->get<ISubcaseService> ()->FindSubCase (subcaseId);
        // get m_dofglobalindex
        Subcase subcase (para);

        SG::FEMSolver::DOFHandler DOFHandler (nodeIds, m_field, Nodeindex);
        DOFHandler.Compute (dbServiceFactory, subcase);
        auto    m_nGlobalMSets  = DOFHandler.GetSlaveDOFSize ();
        int32_t M_nGlobalMSets  = m_nGlobalMSets;
        auto    m_nSparseMatrix = DOFHandler.ComputeSparseMatrixSize () + DOFHandler.GetFixedDOFSize ();
        int32_t M_nSparseMatrix = m_nSparseMatrix;
        // Elastic only; No Thermal
        switch (m_field)
        {
        case Field_Types::FIELD_Elastic:
            s_maxNodeDOFSize = 6;
            break;
        case Field_Types::FIELD_Thermal:
            s_maxNodeDOFSize = 1;
            break;
        default:
            std::cerr << "Cant support field \n";
            break;
        }

        for (size_t i = 0; i < parts; ++i)
        {
            // get nodeid and nodeindex
            auto                     nodeIdsSub = SG::DBManager::DBAPI::GetAllNodeId (dbfMap[i].get ());
            std::vector<std::size_t> indexes (nodeIdsSub.size ());
            for (size_t j = 0; j < nodeIdsSub.size (); ++j)
            {
                indexes[j] = j;
            }
            const DofSet dof = SG::DataStructure::FEM::getAllNodeDofs ();
            // SG::FEMSolver::DOFHandler dofhandler (nodeIdsSub, Field_Types::FIELD_Elastic, indexes);
            // dofhandler.Compute (dbfMap[i], subcase);
            std::vector<SG::DataStructure::FEM::IndexMappingData> datas;
            SG::DataStructure::Common::I64                        m_part;
            for (size_t id = 0; id < nodeIdsSub.size (); ++id)
            {
                for (std::size_t index = 0; index < dof.size (); ++index)
                {
                    auto        it       = std::find (nodeIds.begin (), nodeIds.end (), nodeIdsSub[id]);
                    std::size_t posi     = std::distance (nodeIds.begin (), it);
                    const auto  m_global = DOFHandler.GetGlobalIndex (posi, dof[index]);
                    // const auto  m_part   = dofhandler.GetGlobalIndex (indexes[id], dof[index]);
                    m_part                                        = indexes[id] * s_maxNodeDOFSize + static_cast<std::size_t> (index);
                    SG::DataStructure::FEM::IndexMappingData data = { m_global, m_part };
                    datas.push_back (data);
                }
            }
            dbfMap[i]->get<IIndexMappingService> ()->Add (datas);
            // GetMPCs
            SG::DataStructure::FEM::IndexMapping _OUT pgMap;
            pgMap = dbfMap[i]->get<IIndexMappingService> ()->GetIndexMapping ();

            // Save the degrees of freedom dimension
            auto service = dbfMap[i]->get<IParameterService> ();
            service->SetCustomParameter ("str", std::string ("DOFSPC_MPC"));
            service->SetCustomParameter ("bool", true);
            service->SetCustomParameter ("m_nGlobalMSets", M_nGlobalMSets);
            service->SetCustomParameter ("m_nSparseMatrix", M_nSparseMatrix);
            SG::Utility::SGParameter customParam = service->GetCustomParameter ();
        }
    }

    for (auto&& dbf : dbfMap)
    {
        dbf.second->DisconnectDatabase ();
    }

    for (auto&& db : dbMap)
    {
        dbFactory.DestroyDatabase (db.second);
    }

    dbServiceFactory->DisconnectDatabase ();
    dbFactory.DestroyDatabase (h5db);

    // release Algebra
    SG::Algebra::Finalize ();

    return 0;
}
